﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RooHui.Infrastructure.Entities.EFContext;
using RooHui.Infrastructure.Entities.Enum;
using RooHui.Infrastructure.Entities;
using RooHui.Portal.Biz.Model;
using System.Threading;
using System.Transactions;
using RooHui.Infrastructure.Entities.Entity;
using RooHui.Tool;
using System.Reflection;
using System.Web;
using RooHui.Application.CommonWebLogic;
using RooHui.Infrastructure.Entities.Repository;

namespace RooHui.Portal.Biz
{
    public class ProjectBiz
    {
        private static object commonKey = new object();

        public static ItemListBox<ProjectItemModel> GetProjectList(int page, int pageSize)
        {
            return iCache.Get<ItemListBox<ProjectItemModel>>(
                string.Format("ProjectBiz.GetProjectList_{0}_{1}", page, 10),
                () =>
                {
                    using (Context ctx = new Context())
                    {
                        var statusList = new List<int>(){
                            (int)ProjectStatus.Publish,
                            (int)ProjectStatus.Full,
                            (int)ProjectStatus.Success,
                            (int)ProjectStatus.Repayment,
                            (int)ProjectStatus.Complete
                        };
                        var query = from p in ctx.Project
                                    join pp in ctx.ProjectPolicy
                                      on p.ID equals pp.ProjectId
                                    join o in ctx.Policy
                                      on pp.PolicyId equals o.ID
                                    join g in ctx.GuaranteeCompany
                                      on p.GuaranteeCompanyID equals g.ID
                                    where statusList.Contains(p.Status)
                                    orderby p.ReleaseTime descending
                                    select new
                                    {
                                        p.ID,
                                        p.ProjectTitle,
                                        p.ProductName,
                                        p.ProjectCode,
                                        p.Status,
                                        p.Financing,
                                        p.BeginDate,
                                        p.ReleaseTime,
                                        p.WaitDay,
                                        p.CycleAmount,
                                        o.Rate,
                                        o.SettlementType,
                                        o.BalanceType,
                                        o.Duration,
                                        o.DurationType,
                                        o.IsAttach,
                                        PolicyCycleAmount = o.CycleAmount,
                                        g.CompanyName,
                                        g.LogoImage
                                    };
                        var count = query.GroupBy(c => c.ID).Count();
                        var data = query.Skip(page * pageSize)
                                    .Take(pageSize)
                                    .ToList()
                                    .GroupBy(c => c.ID)
                                    .Select(c => new ProjectItemModel()
                                    {
                                        CompanyName = c.FirstOrDefault().CompanyName,
                                        CycleAmount = c.FirstOrDefault().CycleAmount,
                                        Financing = c.FirstOrDefault().Financing,
                                        LogoImage = c.FirstOrDefault().LogoImage,
                                        ProductName = c.FirstOrDefault().ProductName,
                                        ProjectCode = c.FirstOrDefault().ProjectCode,
                                        ProjectStatus = (ProjectStatus)c.FirstOrDefault().Status,
                                        ProjectId = c.FirstOrDefault().ID,
                                        ProjectTitle = c.FirstOrDefault().ProjectTitle,
                                        ProjectPolicyModel = c.Select(o => new ProjectPolicyModel()
                                        {
                                            BalanceType = (BalanceType)o.BalanceType,
                                            Duration = o.Duration,
                                            DurationType = (DurationType)o.DurationType,
                                            IsAttach = o.IsAttach,
                                            Rate = o.Rate,
                                            SettlementType = (SettlementType)o.SettlementType,
                                            CycleAmount = o.PolicyCycleAmount
                                        }).ToList(),
                                        BeginDate = c.FirstOrDefault().BeginDate,
                                        ReleaseTime = c.FirstOrDefault().ReleaseTime,
                                        WaitDay = c.FirstOrDefault().WaitDay
                                    })
                                    .ToList();
                        data.ForEach(c =>
                        {
                            StringBuilder policyDescription = new StringBuilder();
                            c.ProjectPolicyModel.Where(o => o.IsAttach).ToList().ForEach(i =>
                            {
                                policyDescription.Append(String.Format("<div>奖励 {0} {1} ({2}期)</div>",
                                    i.SettlementType == SettlementType.Proportion ? Math.Round(i.Rate, 4).ToString() + " %" : i.Rate.ToString(),
                                    i.SettlementType == SettlementType.Proportion ? "年化收益率" : "固定收益",
                                    i.CycleAmount == 0 ? c.CycleAmount : i.CycleAmount));
                            });
                            c.PolicyDescription = policyDescription.ToString();
                        });
                        return new ItemListBox<ProjectItemModel>(data).BuildPage(count, page, pageSize, new PageParameter() { Style = "Home" });
                    }
                }, 300);
        }
        public static decimal GetProjectInvestmentRemain(int projectId)
        {
            using (Context ctx = new Context())
            {
                var project = ctx.Project.FirstOrDefault(c => c.ID == projectId && c.Status == (int)ProjectStatus.Publish);
                if (project == null)
                {
                    return 0;
                }
                var data = (from c in ctx.Investment
                            where c.ProjectId == projectId && c.Status != (int)InvestmentStatus.Cancel
                            select new { c.InvestMentAmount })
                           .ToList();
                return project.Financing - data.Sum(c => c.InvestMentAmount);
            }
        }
        /// <summary>
        /// 计算用户剩余投资额（限制单人最大投资额）
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="userName"></param>
        /// <returns></returns>
        public static decimal GetProjectInvestmentRemain(int projectId, string userName)
        {
            using (Context ctx = new Context())
            {
                var project = ctx.Project.FirstOrDefault(c => c.ID == projectId && c.Status == (int)ProjectStatus.Publish);
                if (project == null)
                {
                    return 0;
                }
                var data = (from c in ctx.Investment
                            where c.ProjectId == projectId && c.UserName == userName && c.Status != (int)InvestmentStatus.Cancel
                            select new { c.InvestMentAmount })
                           .ToList();
                return project.MaxInvestment - data.Sum(c => c.InvestMentAmount);
            }
        }
        public static List<ProjectInvestmentScheduleModel> GetProjectInvestmentSchedule(string[] projectIds)
        {
            List<int> idList = new List<int>();
            int id = 0;
            foreach (var item in projectIds)
            {
                if (!String.IsNullOrEmpty(item))
                {
                    if (Int32.TryParse(item, out id))
                    {
                        idList.Add(id);
                    }
                }
            }
            if (!idList.Any())
            {
                return null;
            }
            using (Context ctx = new Context())
            {
                var data = (from c in ctx.Project
                            join iv in ctx.Investment
                             on c.ID equals iv.ProjectId into it
                            from i in it.DefaultIfEmpty()
                            where idList.Contains(c.ID)
                            select new
                            {
                                c.ID,
                                c.Financing,
                                Amount = i == null ? 0 : i.InvestMentAmount,
                                IvStatus = i == null ? (int)InvestmentStatus.Confrim : i.Status
                            }).ToList()
                            .Where(c => c.IvStatus != (int)InvestmentStatus.Cancel)
                            .GroupBy(c => c.ID)
                            .Select(c => new ProjectInvestmentScheduleModel()
                            {
                                ProjectId = c.Key,
                                Total = (c.FirstOrDefault().Financing / 10000).ToString("F2"),
                                Real = ((c.FirstOrDefault().Financing - c.Sum(o => o.Amount)) / 10000).ToString("F2")
                            })
                            .ToList();
                data.ForEach(c =>
                {
                    double percent = (double.Parse(c.Total) - double.Parse(c.Real)) / double.Parse(c.Total);
                    c.Schedule = (Math.Round(percent, 2) * 100).ToString() + "%";
                    c.ProgressStyle = percent <= 0.5 ? "progress-info" : percent < 1 ? "progress-warning" : "progress-success";
                });
                return data;
            }
        }
        public static ProjectDetailModel GetProjectDetail(int projectId)
        {
            using (Context ctx = new Context())
            {
                var statusList = new List<int>(){
                    (int)ProjectStatus.Publish,
                    (int)ProjectStatus.Full,
                    (int)ProjectStatus.Success,
                    (int)ProjectStatus.Repayment,
                    (int)ProjectStatus.Complete
                };

                var data = iCache.Get("partial_ProjectDetail_" + projectId, () =>
                {
                    return (from p in ctx.Project
                            join c in ctx.Enterprise
                             on p.EnterpriseID equals c.ID
                            join g in ctx.GuaranteeCompany
                             on p.GuaranteeCompanyID equals g.ID
                            //join iv in ctx.Investment
                            // on p.ID equals iv.ProjectId
                            join pp in ctx.ProjectPolicy
                             on p.ID equals pp.ProjectId
                            join o in ctx.Policy
                             on pp.PolicyId equals o.ID
                            join r in ctx.Product
                             on p.ProductID equals r.ID
                            join rs in ctx.Resource
                             on p.ID equals rs.RefId
                            where p.ID == projectId && statusList.Contains(p.Status) && rs.Type == (int)ResourceType.ProjectResource
                            select new
                            {
                                ProjectId = p.ID,
                                ProductName = p.ProductName,
                                ProjectCode = p.ProjectCode,
                                ProjectTitle = p.ProjectTitle,
                                ProjectStartDate = p.BeginDate,
                                ProjectReleaseTime = p.ReleaseTime,
                                ProjectEndDate = p.EndDate,
                                ProjectStatus = p.Status,
                                EnterpriseCode = c.EnterpriseCode,
                                EnterpriseCredit = c.EnterpriseCredit,
                                Financing = p.Financing,
                                ProjectCycleAmount = p.CycleAmount,
                                ProjectDescription = p.ProjectDescription,
                                MinInvestment = p.MinInvestment,
                                IncreaseMoney = p.IncreaseMoney,
                                MaxInvestment = p.MaxInvestment,
                                PolicyId = o.ID,
                                CycleAmount = o.CycleAmount,
                                PolicyName = o.PolicyName,
                                Rate = o.Rate,
                                SettlementType = o.SettlementType,
                                Duration = o.Duration,
                                DurationType = o.DurationType,
                                BalanceType = o.BalanceType,
                                IsAttach = o.IsAttach,
                                CompanyName = g.CompanyName,
                                CompanyLogo = g.LogoImage,
                                CompanySummary = g.Summary,
                                ProjectResourceId = rs.ID,
                                ProjectResourceName = rs.Name,
                                ProjectResourcePath = rs.Path,
                                ProjectResourcePL = rs.PrivateLevel,
                                ResouceSubType = rs.SubType
                            }).ToList();
                }, 300);
                if (data != null && data.Any())
                {
                    ProjectDetailModel result = new ProjectDetailModel();
                    var project = data.GroupBy(c => c.ProjectId).FirstOrDefault();
                    //投资记录事实查询
                    var iv = (from p in ctx.Project
                              join i in ctx.Investment
                                 on p.ID equals i.ProjectId
                              where p.ID == projectId && i.Status != (int)InvestmentStatus.Cancel
                              select new
                              {
                                  InvestId = i.ID,
                                  InvestMentAmount = i.InvestMentAmount,
                                  Invester = i.UserName,
                                  InvestDate = i.CreateTime
                              }).ToList();
                    result.ProjectId = project.FirstOrDefault().ProjectId;
                    result.RemainFinancing = project.FirstOrDefault().Financing - iv.Sum(c => c.InvestMentAmount);
                    result.ProjectStatus = (ProjectStatus)project.FirstOrDefault().ProjectStatus;
                    result.ProductName = project.FirstOrDefault().ProductName;
                    result.ProjectCycleAmount = project.FirstOrDefault().ProjectCycleAmount;
                    result.ProjectTitle = project.FirstOrDefault().ProjectTitle;
                    result.ProjectCode = project.FirstOrDefault().ProjectCode;
                    result.Financing = project.FirstOrDefault().Financing;
                    result.Policies = project.GroupBy(c=>c.PolicyId).Select(c => new ProjectPolicyModel()
                    {
                        Id = c.FirstOrDefault().ProjectId,
                        PolicyName = c.FirstOrDefault().PolicyName,
                        BalanceType = (BalanceType)c.FirstOrDefault().BalanceType,
                        Duration = c.FirstOrDefault().Duration,
                        DurationType = (DurationType)c.FirstOrDefault().DurationType,
                        IsAttach = c.FirstOrDefault().IsAttach,
                        Rate = c.FirstOrDefault().Rate,
                        SettlementType = (SettlementType)c.FirstOrDefault().SettlementType,
                        CycleAmount = c.FirstOrDefault().CycleAmount
                    }).ToList();
                    StringBuilder policyDescription = new StringBuilder();
                    result.Policies.Where(o => o.IsAttach).ToList().ForEach(i =>
                    {
                        policyDescription.Append(String.Format("<div>奖励 {0} {1} ({2}期)</div>",
                            i.SettlementType == SettlementType.Proportion ? Math.Round(i.Rate, 4).ToString() + " %" : i.Rate.ToString(),
                            i.SettlementType == SettlementType.Proportion ? "年化收益率" : "固定收益",
                            result.ProjectCycleAmount));
                    });
                    result.PolicyDescription = policyDescription.ToString();

                    result.Invest = iv.Select(c => new ProjectInvestModel()
                    {
                        Id = c.InvestId,
                        Invester = c.Invester,
                        RecordDate = c.InvestDate,
                        InvestMentAmount = c.InvestMentAmount
                    }).OrderByDescending(c => c.Id).ToList();

                    result.GuaranteeCompanyName = project.FirstOrDefault().CompanyName;
                    result.GuaranteeCompanyLogo = project.FirstOrDefault().CompanyLogo;
                    result.GuaranteeCompanySummary = project.FirstOrDefault().CompanySummary;
                    result.ReleaseTime = project.FirstOrDefault().ProjectReleaseTime;
                    result.BeginDate = project.FirstOrDefault().ProjectStartDate;
                    result.EndDate = project.FirstOrDefault().ProjectEndDate;
                    result.MinInvestment = project.FirstOrDefault().MinInvestment;
                    result.MaxInvestment = project.FirstOrDefault().MaxInvestment;
                    result.IncreaseMoney = project.FirstOrDefault().IncreaseMoney;
                    result.ProjectDescription = project.FirstOrDefault().ProjectDescription;
                    result.EnterpriseCode = project.FirstOrDefault().EnterpriseCode;
                    result.EnterpriseCredit = project.FirstOrDefault().EnterpriseCredit;
                    result.RepaymentPlan = RepaymentCalculate(result.BeginDate,
                        result.ProjectCycleAmount, result.Policies, result.Financing);
                    result.Resource = project.GroupBy(c=>c.ProjectResourceId).Select(c => new ProjectResourceModel()
                    {
                        Name = c.FirstOrDefault().ProjectResourceName,
                        Path = c.FirstOrDefault().ProjectResourcePath,
                        PrivateLevel = (PrivateLevel)c.FirstOrDefault().ProjectResourcePL,
                        ResouceSubType = (ResourceSubType)c.FirstOrDefault().ResouceSubType
                    }).ToList();
                    return result;
                }
                return null;
            }
        }
        public static ProjectDetailModel GetOrderDetailWithProject(int projectId)
        {
            using (Context ctx = new Context())
            {
                var data = iCache.Get("partial_GetOrderDetail_" + projectId, () =>
                {
                    return (from r in ctx.Order
                            join p in ctx.Project
                             on r.ProjectId equals p.ID
                            join c in ctx.Enterprise
                             on p.EnterpriseID equals c.ID
                            join g in ctx.GuaranteeCompany
                             on p.GuaranteeCompanyID equals g.ID
                            join op in ctx.OrderPolicy
                             on r.ID equals op.OrderId
                            join o in ctx.Policy
                             on op.PolicyId equals o.ID
                            join pd in ctx.Product
                             on p.ProductID equals pd.ID
                            join rs in ctx.Resource
                             on p.ID equals rs.RefId
                            where r.ProjectId == projectId && rs.Type == (int)ResourceType.ProjectResource
                            select new
                            {
                                ProjectId = p.ID,
                                ProductName = p.ProductName,
                                ProjectCode = p.ProjectCode,
                                ProjectTitle = p.ProjectTitle,
                                ProjectStartDate = p.BeginDate,
                                ProjectEndDate = p.EndDate,
                                ProjectStatus = p.Status,
                                EnterpriseCode = c.EnterpriseCode,
                                EnterpriseCredit = c.EnterpriseCredit,
                                Financing = p.Financing,
                                ProjectCycleAmount = p.CycleAmount,
                                ProjectDescription = p.ProjectDescription,
                                MinInvestment = p.MinInvestment,
                                IncreaseMoney = p.IncreaseMoney,
                                MaxInvestment = p.MaxInvestment,
                                PolicyAddType = op.AddType,
                                PolicyId = o.ID,
                                CycleAmount = o.CycleAmount,
                                PolicyName = o.PolicyName,
                                Rate = o.Rate,
                                SettlementType = o.SettlementType,
                                Duration = o.Duration,
                                DurationType = o.DurationType,
                                BalanceType = o.BalanceType,
                                IsAttach = o.IsAttach,
                                CompanyName = g.CompanyName,
                                CompanyLogo = g.LogoImage,
                                CompanySummary = g.Summary,
                                ProjectResourceId = rs.ID,
                                ProjectResourceName = rs.Name,
                                ProjectResourcePath = rs.Path,
                                ProjectResourcePL = rs.PrivateLevel,
                                ProjectResourceSubType = rs.SubType
                            }).ToList();
                }, 300);
                if (data != null && data.Any())
                {
                    ProjectDetailModel result = new ProjectDetailModel();
                    var project = data.GroupBy(c => c.ProjectId).FirstOrDefault();
                    //投资记录事实查询
                    var iv = (from p in ctx.Project
                              join i in ctx.Investment
                                 on p.ID equals i.ProjectId
                              where i.Status != (int)InvestmentStatus.Cancel
                              select new
                              {
                                  InvestId = i.ID,
                                  InvestMentAmount = i.InvestMentAmount,
                                  Invester = i.UserName,
                                  InvestDate = i.CreateTime
                              }).ToList();
                    result.ProjectId = project.FirstOrDefault().ProjectId;
                    result.RemainFinancing = project.FirstOrDefault().Financing - iv.Sum(c => c.InvestMentAmount);
                    result.ProjectStatus = (ProjectStatus)project.FirstOrDefault().ProjectStatus;
                    result.ProductName = project.FirstOrDefault().ProductName;
                    result.ProjectCycleAmount = project.FirstOrDefault().ProjectCycleAmount;
                    result.ProjectTitle = project.FirstOrDefault().ProjectTitle;
                    result.ProjectCode = project.FirstOrDefault().ProjectCode;
                    result.Financing = project.FirstOrDefault().Financing;
                    result.Policies = project.GroupBy(c => c.PolicyId).Select(c => new ProjectPolicyModel()
                    {
                        Id = c.FirstOrDefault().ProjectId,
                        PolicyName = c.FirstOrDefault().PolicyName,
                        BalanceType = (BalanceType)c.FirstOrDefault().BalanceType,
                        Duration = c.FirstOrDefault().Duration,
                        DurationType = (DurationType)c.FirstOrDefault().DurationType,
                        IsAttach = c.FirstOrDefault().IsAttach,
                        Rate = c.FirstOrDefault().Rate,
                        SettlementType = (SettlementType)c.FirstOrDefault().SettlementType,
                        CycleAmount = c.FirstOrDefault().CycleAmount,
                        AddType = (PolicyAddType)c.FirstOrDefault().PolicyAddType
                    }).ToList();
                    StringBuilder policyDescription = new StringBuilder();
                    result.Policies.Where(o => o.IsAttach).ToList().ForEach(i =>
                    {
                        policyDescription.Append(String.Format("<div>奖励 {0} {1} ({2}期)</div>",
                            i.SettlementType == SettlementType.Proportion ? Math.Round(i.Rate, 4).ToString() + " %" : i.Rate.ToString(),
                            i.SettlementType == SettlementType.Proportion ? "年化收益率" : "固定收益",
                            result.ProjectCycleAmount));
                    });
                    result.PolicyDescription = policyDescription.ToString();

                    result.Invest = iv.Select(c => new ProjectInvestModel()
                    {
                        Id = c.InvestId,
                        Invester = c.Invester,
                        RecordDate = c.InvestDate,
                        InvestMentAmount = c.InvestMentAmount
                    }).OrderByDescending(c => c.Id).ToList();

                    result.GuaranteeCompanyName = project.FirstOrDefault().CompanyName;
                    result.GuaranteeCompanyLogo = project.FirstOrDefault().CompanyLogo;
                    result.GuaranteeCompanySummary = project.FirstOrDefault().CompanySummary;
                    result.BeginDate = project.FirstOrDefault().ProjectStartDate;
                    result.EndDate = project.FirstOrDefault().ProjectEndDate;
                    result.MinInvestment = project.FirstOrDefault().MinInvestment;
                    result.MaxInvestment = project.FirstOrDefault().MaxInvestment;
                    result.IncreaseMoney = project.FirstOrDefault().IncreaseMoney;
                    result.ProjectDescription = project.FirstOrDefault().ProjectDescription;
                    result.EnterpriseCode = project.FirstOrDefault().EnterpriseCode;
                    result.EnterpriseCredit = project.FirstOrDefault().EnterpriseCredit;
                    result.RepaymentPlan = RepaymentCalculate(result.BeginDate,
                        result.ProjectCycleAmount, result.Policies, result.Financing);
                    result.Resource = project.GroupBy(c => c.ProjectResourceId).Select(c => new ProjectResourceModel()
                    {
                        Name = c.FirstOrDefault().ProjectResourceName,
                        Path = c.FirstOrDefault().ProjectResourcePath,
                        PrivateLevel = (PrivateLevel)c.FirstOrDefault().ProjectResourcePL,
                        ResouceSubType = (ResourceSubType)c.FirstOrDefault().ProjectResourceSubType
                    }).ToList();
                    return result;
                }
                return null;
            }
        }
        /// <summary>
        /// 计算投资收益详情
        /// </summary>
        /// <param name="startDate">起息时间</param>
        /// <param name="projectDuration">项目单期步长</param>
        /// <param name="projectDurationType">项目期使用单位</param>
        /// <param name="projectCycleAmount">项目期数</param>
        /// <param name="policies"></param>
        /// <param name="invest">投资金额</param>
        /// <returns></returns>
        public static List<PayBackItemModel> RepaymentCalculate(DateTime startDate,
            int projectCycleAmount, List<ProjectPolicyModel> policies, decimal invest)
        {
            List<PayBackItemModel> plan = new List<PayBackItemModel>();

            //项目策略的期数与项目期数保持一致
            var projectPolicy = policies.First(c => !c.IsAttach);
            projectPolicy.CycleAmount = projectCycleAmount;
            if (projectPolicy.BalanceType == BalanceType.TerminalCapitalInterest)
            { 
                //如果是到期结本息,只应该有一期
                projectPolicy.CycleAmount = 1;
            }

            #region 值修正
            policies.ForEach(c => {
                if (c.IsAttach)
                {
                    //如果是特殊策略
                    if (c.SettlementType == SettlementType.Proportion)
                    {
                        //如果是按百分比计算，则结算数据应和项目保持一致
                        c.Duration = projectPolicy.Duration;
                        c.DurationType = projectPolicy.DurationType;
                        c.BalanceType = projectPolicy.BalanceType;
                        c.CycleAmount = projectPolicy.CycleAmount;
                    }
                    else
                    {
                        //如果是固定值
                        if (c.BalanceType == BalanceType.EverageFixed)
                        {
                            //每月平均方式
                            c.Duration = projectPolicy.Duration;
                            c.DurationType = projectPolicy.DurationType;
                            c.CycleAmount = projectPolicy.CycleAmount;
                        }
                        else if (c.BalanceType == BalanceType.TerminalFixed)
                        {
                            //到期结清
                            //只有一期，且为最后一期
                            c.CycleAmount = 1;
                            c.DurationType = projectPolicy.DurationType;
                            c.Duration = projectPolicy.Duration * projectPolicy.CycleAmount;
                        }
                    }
                }
            });
            #endregion

            //总利息 月利息
            decimal totalRate = 0, totalRateOfMonth = 0;
            //key:期数 value:[key:本金，value:总利息]
            Dictionary<int, KeyValuePair<decimal,decimal>> paybackDict = null;
            //等额本息下每月（期）还款额
            decimal paybackOfMonth = 0;
            if (projectPolicy.BalanceType == BalanceType.AverCapitalInterest)
            {
                totalRate = policies.Where(c => c.BalanceType == BalanceType.AverCapitalInterest && c.SettlementType == SettlementType.Proportion).Sum(c => c.Rate) / 100;

                if (projectPolicy.DurationType == DurationType.ByMonth)
                {
                    totalRateOfMonth = totalRate / 12;
                }

                paybackOfMonth = (invest * totalRateOfMonth * (decimal)Math.Pow((double)(1 + totalRateOfMonth), (double)projectPolicy.CycleAmount)) / (decimal)(Math.Pow((double)(1 + totalRateOfMonth), (double)projectPolicy.CycleAmount) - 1);

                paybackDict = new Dictionary<int, KeyValuePair<decimal, decimal>>();
                for (int i = 0, len = projectPolicy.CycleAmount; i < len; i++)
                {
                    //以还款本金总额
                    decimal paybacked = 0;
                    foreach (var item in paybackDict)
                    {
                        paybacked += item.Value.Key;
                    }

                    //利息部分：
                    decimal cltValue = (invest - paybacked) * totalRateOfMonth;
                    decimal accrual = Math.Round(cltValue, 2, MidpointRounding.AwayFromZero);
                    if (cltValue - accrual >= new decimal(0.005))
                    {
                        accrual += new decimal(0.01);
                    }
                    paybackDict.Add(i, new KeyValuePair<decimal, decimal>(paybackOfMonth - accrual, accrual));
                }
            }

            foreach (var policy in policies)
            {                
                for (int i = 0; i < policy.CycleAmount; i++)
                {
                    PayBackItemModel planItem = new PayBackItemModel();
                    planItem.AddType = policy.AddType;
                    planItem.CycleNumber = i + 1;
                    planItem.Name = !policy.IsAttach ?
                        (policy.BalanceType == BalanceType.EachInterestTerminalCapital ? (i == policy.CycleAmount - 1 ? "本金+利息" : "利息") : "本金+利息")
                        : String.Format("{0} {1} ({2}/{3} 期)",
                        (policy.SettlementType == SettlementType.Proportion ? (policy.Rate.ToString() + " %") : policy.Rate.ToString()), policy.PolicyName, i + 1, policy.CycleAmount);

                    planItem.PolicyId = policy.Id;
                    planItem.Sample = invest;
                    decimal corpus = 0, accrual = 0, cltValue = 0;//本阶段总天数
                    decimal spanDay = 0;
                    if (policy.DurationType == DurationType.ByDay)
                    {
                        planItem.PayBackDate = startDate.AddDays(policy.Duration * (i + 1));
                        spanDay = policy.Duration;
                    }
                    else if (policy.DurationType == DurationType.ByMonth)
                    {
                        planItem.PayBackDate = startDate.AddMonths(policy.Duration * (i + 1));
                        spanDay = startDate.AddMonths(policy.Duration * (i + 1)).Subtract(
                            startDate.AddMonths(policy.Duration * i)).Days;
                    }
                    else
                    {
                        continue;
                    }
                    switch (policy.BalanceType)
                    {
                        //每期等额本息
                        case BalanceType.AverCapitalInterest:

                            //利息部分
                            cltValue = paybackDict[i].Value * ((policy.Rate / 100) / totalRate);
                            accrual = Math.Round(cltValue, 2, MidpointRounding.AwayFromZero);
                            if (cltValue - accrual >= new decimal(0.005))
                            {
                                accrual += new decimal(0.01);
                            }
                            if (!policy.IsAttach)
                            {
                                //本金部分
                                cltValue = paybackDict[i].Key;
                                corpus = Math.Round(cltValue, 2, MidpointRounding.AwayFromZero);
                                if (cltValue - corpus >= new decimal(0.005))
                                {
                                    corpus += new decimal(0.01);
                                }
                            }
                            break;
                        //每期结息到期还本
                        case BalanceType.EachInterestTerminalCapital:
                            if (!policy.IsAttach && i == policy.CycleAmount - 1)
                            { 
                                //如果是非特殊策略
                                //如果是最后一期
                                //本金部分
                                corpus = invest;
                            }
                            //利息部分
                            cltValue = invest * ((policy.Rate / 100) / 365) * spanDay;
                            accrual = Math.Round(cltValue, 2, MidpointRounding.AwayFromZero);
                            if (cltValue - accrual >= new decimal(0.005))
                            {
                                accrual += new decimal(0.01);
                            }
                            break;
                        //到期结本息 此模式下，期数为1
                        case BalanceType.TerminalCapitalInterest:
                            if (!policy.IsAttach && i == policy.CycleAmount - 1)
                            {
                                //本金部分
                                corpus = invest;
                            }
                            //利息部分
                            cltValue = invest * ((policy.Rate / 100) / 365) * spanDay;
                            accrual = Math.Round(cltValue, 2, MidpointRounding.AwayFromZero);
                            if (cltValue - accrual >= new decimal(0.005))
                            {
                                accrual += new decimal(0.01);
                            }            
                            break;
                        //BalanceType=2 固定值
                        //每月平均，此模式一定不会是非特殊策略
                        case BalanceType.EverageFixed:
                            //本金部分
                            corpus = 0;
                            //利息部分
                            cltValue = policy.Rate / policy.CycleAmount;
                            accrual = Math.Round(cltValue, 2, MidpointRounding.AwayFromZero);
                            if (cltValue - accrual >= new decimal(0.005))
                            {
                                accrual += new decimal(0.01);
                            }
                            break;
                        //BalanceType=2 固定值
                        //到期付清，此模式一定不会是非特殊策略
                        case BalanceType.TerminalFixed:
                            //本金部分
                            corpus = 0;
                            //利息部分
                            accrual = policy.Rate;
                            break;
                    }
                    planItem.Value = corpus + accrual;
                    plan.Add(planItem);
                }
            }
            return plan;
        }
        public static List<PayBackItemModel> RepaymentCalculate(int projectId, decimal invest)
        {
            using (Context ctx = new Context())
            {
                var data = (from p in ctx.Project
                            join pp in ctx.ProjectPolicy
                             on p.ID equals pp.ProjectId
                            join o in ctx.Policy
                             on pp.PolicyId equals o.ID
                            where p.ID == projectId
                            select new
                            {
                                ProjectId = p.ID,
                                ProjectBeginData = p.BeginDate,
                                ProjectEndDate = p.EndDate,
                                ProjectCycleAmount = p.CycleAmount,
                                PolicyId = o.ID,
                                CycleAmount = o.CycleAmount,
                                PolicyName = o.PolicyName,
                                Rate = o.Rate,
                                SettlementType = o.SettlementType,
                                Duration = o.Duration,
                                DurationType = o.DurationType,
                                BalanceType = o.BalanceType,
                                IsAttach = o.IsAttach
                            }).ToList();
                if (!data.Any())
                {
                    return null;
                }
                var policies = data.GroupBy(c => c.PolicyId).Select(c => new ProjectPolicyModel()
                {
                    Id = c.FirstOrDefault().ProjectId,
                    PolicyName = c.FirstOrDefault().PolicyName,
                    BalanceType = (BalanceType)c.FirstOrDefault().BalanceType,
                    Duration = c.FirstOrDefault().Duration,
                    DurationType = (DurationType)c.FirstOrDefault().DurationType,
                    IsAttach = c.FirstOrDefault().IsAttach,
                    Rate = c.FirstOrDefault().Rate,
                    SettlementType = (SettlementType)c.FirstOrDefault().SettlementType,
                    CycleAmount = c.FirstOrDefault().CycleAmount == 0 ? 
                        data.FirstOrDefault().ProjectCycleAmount : 
                        c.FirstOrDefault().CycleAmount
                }).ToList();
                var project = data.FirstOrDefault();
                return RepaymentCalculate(project.ProjectBeginData, project.ProjectCycleAmount,
                    policies, invest);
            }
        }
        
        /// <summary>
        /// 计算真实投资收益计划
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="invest"></param>
        /// <returns></returns>
        public static List<PayBackItemModel> InvestmentRepaymentCalculate(int projectId, decimal invest)
        {
            using (Context ctx = new Context())
            {
                var data = (from j in ctx.Project
                            join o in ctx.Order
                             on j.ID equals o.ProjectId
                            join op in ctx.OrderPolicy
                             on o.ID equals op.OrderId
                            join p in ctx.Policy
                             on op.PolicyId equals p.ID
                            where j.ID == projectId
                            select new
                            {
                                ProjectId = j.ID,
                                ProjectBeginData = j.BeginDate,
                                ProjectEndDate = j.EndDate,
                                ProjectCycleAmount = j.CycleAmount,
                                PolicyId = p.ID,
                                CycleAmount = p.CycleAmount,
                                PolicyName = p.PolicyName,
                                Rate = p.Rate,
                                SettlementType = p.SettlementType,
                                Duration = p.Duration,
                                DurationType = p.DurationType,
                                BalanceType = p.BalanceType,
                                IsAttach = p.IsAttach,
                                AddType = op.AddType
                            }).ToList();
                if (!data.Any())
                {
                    return null;
                }
                var policies = data.GroupBy(c => c.PolicyId).Select(c => new ProjectPolicyModel()
                {
                    Id = c.FirstOrDefault().ProjectId,
                    PolicyName = c.FirstOrDefault().PolicyName,
                    BalanceType = (BalanceType)c.FirstOrDefault().BalanceType,
                    Duration = c.FirstOrDefault().Duration,
                    DurationType = (DurationType)c.FirstOrDefault().DurationType,
                    IsAttach = c.FirstOrDefault().IsAttach,
                    Rate = c.FirstOrDefault().Rate,
                    SettlementType = (SettlementType)c.FirstOrDefault().SettlementType,
                    CycleAmount = c.FirstOrDefault().CycleAmount == 0 ?
                        data.FirstOrDefault().ProjectCycleAmount :
                        c.FirstOrDefault().CycleAmount,
                    AddType = (PolicyAddType)c.FirstOrDefault().AddType
                }).ToList();
                var project = data.FirstOrDefault();
                return RepaymentCalculate(project.ProjectBeginData, 
                    project.ProjectCycleAmount, policies, invest);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="projectId"></param>
        /// <param name="userName">投资者</param>
        /// <param name="invest"></param>
        /// <returns>
        /// 9:成功 
        /// 0：项目数据非法 -1:投资已满 -2:投资余额不足 -3:投资额必须为单份金额整数倍 -4:投资超限额（单人最大投资额） -5:投资额超过项目剩余额度 -9:投标失败，请稍后重试
        /// -10:账户实名未通过
        /// -11:账户未激活
        /// -12:必须为投资人账号
        /// -13:账户不可用
        /// </returns>
        public static int DoInvestment(int projectId, string userName, decimal invest)
        {
            //step1.验证基本信息
            if (invest <= 0)
            {
                return 0;
            }
            var member = new Member().Find(userName);
            if (member == null)
            {
                return 0;
            }
            else
            {
                if (!member.IDCardCheck)
                {
                    return -10;
                }
                if (!member.IsActive)
                {
                    return -11;
                }
                if (member.MemberType != MemberType.Invester)
                {
                    return -12;
                }
                if (member.Status != ItemStatus.Enable)
                {
                    return -13;
                }
            }
            bool isSuccess = false; //执行是否成功
            var project = new Project().Find(c => c.ID == projectId && c.Status == (int)ProjectStatus.Publish);
            if (project!= null)
            {
                //获取投资同步锁
                using (CriticalRegion region = new CriticalRegion(projectId.ToString()))
                {
                    if (region.Set())
                    {
                        if (project == null || project.Status != ProjectStatus.Publish || project.ReleaseTime > DateTime.Now || project.Financing < invest)
                        {
                            return 0;
                        }
                        var rm = ProjectBiz.GetProjectInvestmentRemain(project.ID, userName);
                        if (rm < invest)
                        {
                            return -4;
                        }
                        try
                        {
                            using (TransactionScope scope = new TransactionScope())
                            {
                                using (Context ctx = new Context())
                                {
                                    int changeCode = TallyForInvestment(ctx, userName, project, invest);
                                    if (changeCode != 9)
                                    {
                                        return changeCode;
                                    }
                                    var order = new Order().Find(c => c.ProjectId == project.ID && c.UserName == userName);
                                    bool addPolicySuccess = true;
                                    if (order == null)
                                    {
                                        //针对单个项目，只生成一条订单数据。可生成N条投资数据
                                        //绑定策略针对订单，仅在第一次生成order数据时绑定策略信息
                                        //单个项目仅在第一次投保时验证是否符合条件。一旦验证成功，所有的所有投标均适用
                                        var promotionPolicies = GetOrderPromotionPolicies(project.ID, invest, userName);
                                        //生成订单数据
                                        order = new Order()
                                        {
                                            CreateTime = DateTime.Now,
                                            Amount = invest,
                                            ProjectId = project.ID,
                                            OrderCode = GenerateOrderCode("100"),
                                            Status = OrderStatus.Fresh,
                                            UserName = userName
                                        }.PostAdd();
                                        //读取项目绑定的策略
                                        var projectPolicies = (from pp in ctx.ProjectPolicy
                                                               join p in ctx.Policy
                                                                 on pp.PolicyId equals p.ID
                                                               where pp.ProjectId == project.ID
                                                               select new { PolicyId = p.ID, IsAttach = p.IsAttach }).ToList();

                                        //先添加促销策略
                                        List<int> ContainPolicy = new List<int>();
                                        foreach (var item in promotionPolicies)
                                        {
                                            //先添加促销添加的策略
                                            var policyList = item.AttachPolicy.ToObject<List<int>>();
                                            foreach (var policyId in policyList)
                                            {
                                                if (ContainPolicy.Contains(policyId))
                                                {
                                                    //不能重复添加策略
                                                    continue;
                                                }
                                                ContainPolicy.Add(policyId);
                                                //添加策略 
                                                if (new OrderPolicy()
                                                {
                                                    AddType = PolicyAddType.Promotion,
                                                    OrderId = order.ID,
                                                    PolicyId = policyId,
                                                    RefId = item.ID
                                                }.PostAdd() == null)
                                                {
                                                    addPolicySuccess = false;
                                                    break;
                                                }
                                            }
                                            if (!addPolicySuccess)
                                            {
                                                break;
                                            }
                                        }
                                        //添加项目绑定的策略
                                        foreach (var item in projectPolicies)
                                        {
                                            if (ContainPolicy.Contains(item.PolicyId))
                                            {
                                                //策略不能重复添加
                                                continue;
                                            }
                                            //添加项目绑定的策略 
                                            if (new OrderPolicy()
                                            {
                                                AddType = item.IsAttach ? PolicyAddType.Award : PolicyAddType.Standard,
                                                OrderId = order.ID,
                                                PolicyId = item.PolicyId,
                                                RefId = 0
                                            }.PostAdd() == null)
                                            {
                                                addPolicySuccess = false;
                                                break;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        //如果不是第一次投资本项目，则只需要更新投资金额
                                        order.Amount += invest;
                                        order = order.PostModify();
                                    }
                                    if (order != null)
                                    {
                                        //一个订单数据，N条投资数据
                                        if (addPolicySuccess)
                                        {
                                            //生成投资数据
                                            var investment = new Investment()
                                            {
                                                CreateTime = DateTime.Now,
                                                InvestMentAmount = invest,
                                                InvestMentCode = GetInvestmentCode(projectId).ToString(),
                                                OrderID = order.ID,
                                                PayWay = PayWay.HuiFu,
                                                ProjectId = projectId,
                                                Status = InvestmentStatus.Confrim,
                                                UserName = userName
                                            }.PostAdd();
                                            if (investment != null)
                                            {
                                                //如果已经满标，修改项目状态为满标
                                                if (GetProjectInvestmentRemain(projectId) <= 0)
                                                {
                                                    new ProjectHistory()
                                                    {
                                                        ChangeContent = "项目满标",
                                                        CreateTime = DateTime.Now,
                                                        CreateUser = "system auto",
                                                        NewStatus = ProjectStatus.Full,
                                                        PreState = project.Status,
                                                        ProjectId = project.ID
                                                    }.PostAdd();
                                                    project.Status = ProjectStatus.Full;
                                                    project.FullScaleTime = DateTime.Now;
                                                    project.PostModify();
                                                    //满标后天等待后台确认后执行解冻资金，放款
                                                }
                                                //提交事务
                                                scope.Complete();
                                                isSuccess = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        catch (Exception e)
                        {
                            LogHelper.Write(CommonLogger.Web, LogLevel.Error, e);
                        }
                    }
                }
            }
            return isSuccess ? 9 : -9;
        }

        /// <summary>
        /// 投资交互
        /// </summary>
        /// <returns></returns>
        private static int TallyForInvestment(Context ctx, string userName, Project project, decimal amount)
        {
            //step2. 验证账户余额
            var account1 = MemberBiz.GetAccount(userName); //投资前账户余额
            if (account1.BalanceAmount < amount)
            {
                return -2;
            }
            //step3. 验证投资信息
            //单份投资金额
            decimal increaseMoney = project.IncreaseMoney;
            //项目剩余可投资额
            decimal remain = ProjectBiz.GetProjectInvestmentRemain(project.ID);
            if (remain <= 0)
            {
                return -1;
            }
            if (remain < amount)
            {
                return -5;
            }
            if (amount % increaseMoney != 0 && remain < increaseMoney && amount != remain)
            {
                //投资额不为单份金额的整数倍
                //剩余投资额不足单份金额
                //投资额不为剩余全额
                return -3;
            }

            //TODO:调用第三方执行投标（冻结金额）
            //处理冻结失败或者余额不足
            //TODO:(成功)同步账户余额
            if (true)
            {
                var account2 = Account.ChangeAccount(userName, -amount, amount);
                if (account2 != null)
                {
                    var amountOperation = new AmountOperation()
                    {
                        UserName = userName,
                        Amount = amount,
                        CreateTime = DateTime.Now,
                        IpAddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"],
                        IsSuccess = true,
                        OperationType = AmountOperationType.FreezeForProject,
                        SplitAmount = "", //TODO:填写第三方发送指令串
                        ResultCode = "", //TODO:填写第三方返回指令
                        ResultNote = "", //TODO:填写第三方返回指令
                        PayBackTime = DateTime.Now,//TODO:填写第三方返回时间
                        Status = ItemStatus.Enable,
                        RefId = project.ID.ToString(),
                        Summary = String.Format("项目投资： {0} {1}，项目编号：{2}", project.ProductName, project.ProjectTitle, project.ProjectCode)
                    }.PostAdd();

                    if (amountOperation != null)
                    {
                        var accountHis = new AmountHistory()
                        {
                            AmountOperationID = amountOperation.ID,
                            ChangeValue = amount,
                            CreateTime = DateTime.Now,
                            CurrentAmount = account2.BalanceAmount,
                            CurrentFreeze = account2.FrozenAmount,
                            OperationType = AmountOperationType.Investment,
                            PreAmount = account1.BalanceAmount,
                            PreFreeze = account1.FrozenAmount,
                            TransactionUser = project.BorrowerName,
                            UserName = userName
                        }.PostAdd();
                        return accountHis != null ? 9 : -9;
                    }
                }
            }
            else
            { 
                //TODO:处理失败
            }
            return -9;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="orderType">100：投标订单</param>
        /// <returns></returns>
        private static string GenerateOrderCode(string orderType)
        {
            return String.Format("{0}{1}{2}", orderType, 
                DateTime.Now.ToString("yyMMddHHmmss"), 
                new Random().Next(0, 9999).ToString().PadLeft(4));
        }
        private static int GetInvestmentCode(int projectId)
        {
            return new Investment().Count(c => c.ProjectId == projectId && c.Status != (int)InvestmentStatus.Cancel);
        }
        #region 促销相关
        public static List<PromotionPolicy> GetAllEnablePromotionPolicies()
        {
            return iCache.Get<List<PromotionPolicy>>("ProjectBiz.GetAllEnablePromotionPolicies",
                () =>
                {
                    using (Context ctx = new Context())
                    {
                        return new PromotionPolicy().FindAll(
                            c => c.Enable && c.BeginDate < DateTime.Now && c.EndDate > DateTime.Now && c.Range == (int)PromotionRange.ProjectInvestment);
                    }
                }, 6000);
        }
        /// <summary>
        /// 计算本次投资是否符合促销规则，以添加策略
        /// </summary>
        /// <param name="invest">投资金额</param>
        /// <param name="invester">投资者</param>
        /// <returns></returns>
        public static List<PromotionPolicy> GetOrderPromotionPolicies(int projectId, decimal amount, string invester)
        {
            //仅仅在某人第一次投资某个项目时为其计算是否符合促销条件，并绑定促销政策
            if (new Order().Any(c => c.ProjectId == projectId && c.UserName == invester))
            {
                //如果已经存在过投资记录
                return new List<PromotionPolicy>();
            }
            var promotions = GetAllEnablePromotionPolicies();
            List<PromotionPolicy> result = new List<PromotionPolicy>();
            foreach (var item in promotions)
            {
                if (AuditingInvestmentForPromotion(item.ActiveCode, amount))
                {
                    result.Add(item);
                }
            }
            return result;
        }
        private static object externalDllInstance = null;
        private static MethodInfo externalDllMethod = null;
        private static bool AuditingInvestmentForPromotion(int activeCode,decimal amount)
        {
            if (externalDllInstance == null || externalDllMethod == null)
            {
                lock (commonKey)
                {
                    if (externalDllInstance == null || externalDllMethod == null)
                    {
                        var assembly = Assembly.LoadFile(HttpContext.Current.Server.MapPath("~/bin/") + "RooHui.External.Promotion.dll");
                        if (assembly != null)
                        {
                            Type type = assembly.GetType("RooHui.External.Promotion.PromotionFactory");
                            externalDllInstance = Activator.CreateInstance(type);
                            externalDllMethod = type.GetMethod("Excutor");
                            
                        }
                    }
                }
            }

            var result = externalDllMethod.Invoke(externalDllInstance, new object[] { activeCode, amount });
            bool r = false;
            Boolean.TryParse(result.ToString(),out r);
            return r;
        }
        #endregion
              
    }
}
